home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 6 / develop 6 code / TCP / NewsWatcher / NewsWatcher 2.0d15 source / source / prefs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-30  |  17.9 KB  |  665 lines  |  [TEXT/KAHL]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     prefs.c
  4.  
  5.     This module handles locating, reading, and writing the prefs file, and
  6.     initialization of the preferences if no prefs file exists or an old
  7.     version is encountered.
  8.     
  9.     Portions copyright © 1990, Apple Computer.
  10.     Portions copyright © 1993, Northwestern University.
  11.  
  12. ----------------------------------------------------------------------------*/
  13.  
  14. #include <Packages.h>
  15. #include <Folders.h>
  16. #include <string.h>
  17. #include <stdio.h>
  18.  
  19. #include "glob.h"
  20. #include "prefs.h"
  21. #include "dlgutil.h"
  22. #include "util.h"
  23. #include "full.h"
  24.  
  25.  
  26.  
  27. #define kServerDlg            132            /* Server dialog */
  28. #define kNewsServer            6
  29. #define kMailServer            8
  30.  
  31. #define kPersonalDlg        138            /* Personal info dialog */
  32. #define kFullName            6
  33. #define kOrganization        8
  34. #define kAddress            10
  35.  
  36.  
  37.  
  38.  
  39. static Str255 gNewsServerAtStartup;        /* news server address at startup */
  40.  
  41.  
  42.  
  43. /*----------------------------------------------------------------------------
  44.     GetDefaultTextDir
  45.     
  46.     Return a reasonable place for storing text files when the
  47.     user hasn't selected one yet. Uses the root directory of the 
  48.     volume where NewsWatcher lives.
  49.     
  50.     Exit:    volName = default volume name for saved text files.
  51.             dirID = default dirID for saved text files.
  52. ----------------------------------------------------------------------------*/
  53.  
  54. static void GetDefaultTextDir (Str31 volName, long *dirID)
  55. {
  56.     FCBPBRec pbFcb;
  57.     HParamBlockRec pbVol;
  58.     OSErr err = noErr;
  59.  
  60.     /* Find the volume that we're running on */
  61.     
  62.     pbFcb.ioNamePtr = nil;
  63.     pbFcb.ioRefNum = CurResFile();
  64.     pbFcb.ioFCBIndx = 0;
  65.     err = PBGetFCBInfo(&pbFcb, false);
  66.     
  67.     /* Now find the volume name. */
  68.     
  69.     if (err == noErr) {
  70.         pbVol.volumeParam.ioNamePtr = volName;
  71.         pbVol.volumeParam.ioVRefNum = pbFcb.ioFCBVRefNum;
  72.         pbVol.volumeParam.ioVolIndex = 0;
  73.         err = PBHGetVInfo(&pbVol, false);
  74.     }
  75.  
  76.     if (err != noErr) *volName = 0;
  77.  
  78.     *dirID = fsRtDirID;
  79. }
  80.  
  81.  
  82.  
  83. /*----------------------------------------------------------------------------
  84.     DoServerDialog 
  85.     
  86.     Presents the server dialog.
  87.     
  88.     Entry:    startup = true if initialization call.
  89.     
  90.     Exit:    function result = true if OK clicked, false if Cancel clicked.
  91.             gPrefs.newsServerName = news server name.
  92.             gPrefs.mailServerName = mail server name.
  93. ----------------------------------------------------------------------------*/
  94.  
  95. static Boolean DoServerDialog (void)
  96. {
  97.     DialogPtr dlg;
  98.     short item;
  99.     Str255 news;
  100.     Str255 mail;
  101.     
  102.     dlg = MyGetNewDialog(kServerDlg);
  103.     pstrcpy(news, gPrefs.newsServerName);
  104.     DlgSetPString(dlg, kNewsServer, news);
  105.     SetItemMaxLength(dlg, kNewsServer, 255);
  106.     pstrcpy(mail, gPrefs.mailServerName);
  107.     DlgSetPString(dlg, kMailServer, mail);
  108.     SetItemMaxLength(dlg, kMailServer, 255);
  109.     if (*news == 0) {
  110.         SelIText(dlg, kNewsServer, 0, 0);
  111.     } else if (*mail == 0) {
  112.         SelIText(dlg, kMailServer, 0, 0);
  113.     } else {
  114.         SelIText(dlg, kNewsServer, 0, 0x7fff);
  115.     }
  116.     
  117.     do {
  118.         DlgEnableItem(dlg, ok, *news != 0 && *mail != 0);
  119.         MyModalDialog(DialogFilter, &item, true, true);
  120.         switch (item) {
  121.             case kNewsServer:
  122.                 DlgGetPString(dlg, item, news);
  123.                 break;
  124.             case kMailServer:
  125.                 DlgGetPString(dlg, item, mail);
  126.                 break;
  127.         }
  128.     } while (item != ok && item != cancel);
  129.  
  130.     MyDisposDialog(dlg);
  131.     if (item == ok) {
  132.         pstrcpy(gPrefs.newsServerName, news);
  133.         pstrcpy(gPrefs.mailServerName, mail);
  134.     }
  135.     return item == ok;
  136. }
  137.  
  138.  
  139.  
  140. /*----------------------------------------------------------------------------
  141.     DoPersonalDialog 
  142.     
  143.     Presents the personal information dialog.
  144.     
  145.     Exit:    function result = true if OK clicked, false if Cancel clicked.
  146.             gPrefs.fullName = user's full name.
  147.             gPrefs.organization = user's organization.
  148.             gPrefs.address = user's email address
  149. ----------------------------------------------------------------------------*/
  150.  
  151. static Boolean DoPersonalDialog (void)
  152. {
  153.     DialogPtr dlg;
  154.     short item;
  155.     CStr255 fullName;
  156.     CStr255    organization;
  157.     CStr255 address;
  158.     
  159.     dlg = MyGetNewDialog(kPersonalDlg);
  160.     strcpy(fullName, gPrefs.fullName);
  161.     DlgSetCString(dlg, kFullName, fullName);
  162.     SetItemMaxLength(dlg, kFullName, 255);
  163.     strcpy(organization, gPrefs.organization);
  164.     DlgSetCString(dlg, kOrganization, organization);
  165.     SetItemMaxLength(dlg, kOrganization, 255);
  166.     strcpy(address, gPrefs.address);
  167.     DlgSetCString(dlg, kAddress, address);
  168.     SetItemMaxLength(dlg, kAddress, 255);
  169.     if (*fullName == 0) {
  170.         SelIText(dlg, kFullName, 0, 0);
  171.     } else if (*organization == 0) {
  172.         SelIText(dlg, kOrganization, 0, 0);
  173.     } else if (*address == 0) {
  174.         SelIText(dlg, kAddress, 0, 0);
  175.     } else {
  176.         SelIText(dlg, kFullName, 0, 0x7fff);
  177.     }
  178.     
  179.     do {
  180.         DlgEnableItem(dlg, ok, *address != 0);
  181.         MyModalDialog(DialogFilter, &item, true, true);
  182.         switch (item) {
  183.             case kFullName:
  184.                 DlgGetCString(dlg, item, fullName);
  185.                 break;
  186.             case kOrganization:
  187.                 DlgGetCString(dlg, item, organization);
  188.                 break;
  189.             case kAddress:
  190.                 DlgGetCString(dlg, item, address);
  191.                 break;
  192.         }
  193.     } while (item != ok && item != cancel);
  194.  
  195.     MyDisposDialog(dlg);
  196.     if (item == ok) {
  197.         strcpy(gPrefs.fullName, fullName);
  198.         strcpy(gPrefs.organization, organization);
  199.         strcpy(gPrefs.address, address);
  200.     }
  201.     return item == ok;
  202. }
  203.  
  204.  
  205.  
  206. /*----------------------------------------------------------------------------
  207.     LocatePrefsFile
  208.     
  209.     Locates the preferences file. It also takes care of locating, moving, 
  210.     and renaming any old prefs file from previous versions, if necessary. 
  211.     Note that this function does not open or read the file, or create it 
  212.     if it doesn't already exist.
  213.     
  214.     Entry:    docList = initial document list.
  215.             numDocs = number of documents in list.
  216.  
  217.     Exit:    gPrefsFile = FSSpec recording location of prefs file.
  218. ----------------------------------------------------------------------------*/
  219.  
  220. void LocatePrefsFile (FSSpec **docList, short numDocs)
  221. {
  222.     OSErr err;
  223.     FInfo fndrInfo;
  224.     FSSpec oldFile;
  225.     FSSpec theFile;
  226.     short i;
  227.     FCBPBRec pBlock;
  228.     FSSpec prefsFolder;
  229.     CInfoPBRec pb;
  230.     
  231.     /* Check to see if a NewsWatcher prefs file is an element of
  232.        the document list. If there is, use the first one. */
  233.  
  234.     for (i = 0; i < numDocs; i++) {
  235.         theFile = (*docList)[i];
  236.         err = FSpGetFInfo(&theFile, &fndrInfo);
  237.         if (err == noErr && fndrInfo.fdCreator == kFCreator && fndrInfo.fdType == kPrefType) {
  238.             gPrefsFile = theFile;
  239.             return;
  240.         }
  241.     }
  242.     
  243.     /* Check to see if a NewsWatcher user group list file is an element
  244.        of the document list. If it is, check to see if a "NewsWatcher
  245.        Preferences" file exists in the same folder as the first one. If 
  246.        there is such a prefs file, use that one. */
  247.        
  248.     for (i  = 0; i < numDocs; i++) {
  249.         theFile = (*docList)[i];
  250.         err = FSpGetFInfo(&theFile, &fndrInfo);
  251.         if (err == noErr && fndrInfo.fdCreator == kFCreator && fndrInfo.fdType == kFType) {
  252.             err = FSMakeFSSpec(theFile.vRefNum, theFile.parID, kPrefName, &gPrefsFile);
  253.             if (err == noErr) {
  254.                 err = FSpGetFInfo(&gPrefsFile, &fndrInfo);
  255.                 if (err == noErr) goto exit;
  256.             }
  257.         }
  258.     }
  259.     
  260.     /* If the doc list is empty, check to see if a "NewsWatcher Preferences" file 
  261.        exists in the same folder as the NewsWatcher application. If there is such a 
  262.        prefs file, use that one. */
  263.        
  264.     if (numDocs == 0) {
  265.         pBlock.ioNamePtr = nil;
  266.         pBlock.ioVRefNum = 0;
  267.         pBlock.ioRefNum = CurApRefNum;
  268.         pBlock.ioFCBIndx = 0;
  269.         err = PBGetFCBInfo(&pBlock, false);
  270.         if (err == noErr) {
  271.             err = FSMakeFSSpec(pBlock.ioFCBVRefNum, pBlock.ioFCBParID, kPrefName, &gPrefsFile);
  272.             if (err == noErr) {
  273.                 err = FSpGetFInfo(&gPrefsFile, &fndrInfo);
  274.                 if (err == noErr) goto exit;
  275.             }
  276.         }
  277.     }
  278.     
  279.     /* Construct gPrefsFile = FSSpec for "NewsWatcher Preferences" file
  280.        in Preferences folder. */
  281.  
  282.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType,
  283.         kCreateFolder, &gPrefsFile.vRefNum, &gPrefsFile.parID);
  284.     if (err != noErr) {
  285.         ErrorMessage("Internal error - FindFolder failed.");
  286.         ExitToShell();
  287.     }
  288.     pstrcpy(gPrefsFile.name, kPrefName);
  289.     
  290.     /* Check to see if "NewsWatcher Preferences" exists in the Preferences
  291.        folder. If it does exist, use that one. */
  292.        
  293.     err = FSpGetFInfo(&gPrefsFile, &fndrInfo);
  294.     if (err == noErr) goto exit;
  295.     
  296.     /* If "NewsWatcher Preferences" does not exist in the Preferences
  297.        folder, next try "News Prefs" in the Preferences folder. If that
  298.        exists, rename it "NewsWatcher Preferences". */
  299.        
  300.     err = FSMakeFSSpec(gPrefsFile.vRefNum, gPrefsFile.parID, kOldPrefName, &oldFile);
  301.     if (err == noErr) {
  302.         FSpRename(&oldFile, kPrefName);
  303.         return;
  304.     }
  305.     
  306.     /* If this fails, next try "News Prefs" in the System folder. If that
  307.        exists, move it to the Preferences folder and rename it "NewsWatcher
  308.        Preferences". */
  309.        
  310.     err = FindFolder(kOnSystemDisk, kSystemFolderType,
  311.         kCreateFolder, &oldFile.vRefNum, &oldFile.parID);
  312.     if (err != noErr) return;
  313.     pb.dirInfo.ioNamePtr = prefsFolder.name;
  314.     pb.dirInfo.ioVRefNum = gPrefsFile.vRefNum;
  315.     pb.dirInfo.ioFDirIndex = -1;
  316.     pb.dirInfo.ioDrDirID = gPrefsFile.parID;
  317.     err = PBGetCatInfo(&pb, false);
  318.     if (err != noErr) return;
  319.     prefsFolder.vRefNum = pb.dirInfo.ioVRefNum;
  320.     prefsFolder.parID = pb.dirInfo.ioDrParID;
  321.     err = FSpCatMove(&oldFile, &prefsFolder);
  322.     if (err != noErr) return;
  323.     oldFile.vRefNum = gPrefsFile.vRefNum;
  324.     oldFile.parID = gPrefsFile.parID;
  325.     err = FSpRename(&oldFile, kPrefName);
  326.     if (err != noErr) return;
  327.     err = FSpGetFInfo(&gPrefsFile, &fndrInfo);
  328.     if (err != noErr) return;
  329.     fndrInfo.fdFlags &= ~(1 << 8);    /* clear hasBeenInited to force Finder to
  330.                                          assign new icon location */
  331.     FSpSetFInfo(&gPrefsFile, &fndrInfo);
  332.     return;
  333.     
  334. exit:
  335.  
  336.     if (fndrInfo.fdCreator == kFCreator && fndrInfo.fdType == kPrefType) return;
  337.     ErrorMessage("The owners of this shared Macintosh insist that you open NewsWatcher using a personal preferences file. Ask them for help, or see the NewsWatcher document.");
  338.     ExitToShell();
  339.  
  340. }
  341.  
  342.  
  343.  
  344. /*----------------------------------------------------------------------------
  345.     ScramblePW
  346.     
  347.     Scrambles (and unscrambles) saved passwords. This is not really secure,
  348.     just something to foil people browsing using disk editors.
  349.     
  350.     Entry:    pw = the password.
  351.             len = length of password.
  352.             
  353.     Exit:    Each byte nibble-swapped and bit-flipped.
  354. ----------------------------------------------------------------------------*/
  355.  
  356. static void ScramblePW (char *pw, short len)
  357. {
  358.     char *p, *pEnd;
  359.     
  360.     pEnd = pw + len;
  361.     for (p = pw; p < pEnd; p++) *p = (((*p >> 4) & 0x0f) | ((*p & 0x0f) << 4)) ^ 0xff;
  362. }
  363.  
  364.  
  365.  
  366. /*----------------------------------------------------------------------------
  367.     ReadPrefs 
  368.     
  369.     Reads the preferences file.
  370.     
  371.     Exit:    function result = true if no error, false if error.
  372. ----------------------------------------------------------------------------*/
  373.  
  374. Boolean ReadPrefs (void)
  375. {
  376.     OSErr err;
  377.     short fRefNum = 0;
  378.     long count;
  379.     Boolean need102Defaults = true;
  380.     Boolean need12NUDefaults = true;
  381.     Boolean need13d1Defaults = true;
  382.     Boolean need13d4Defaults = true;
  383.     Boolean need20d9Defaults = true;
  384.     Boolean need20d10Defaults = true;
  385.     Boolean need20d11Defaults = true;
  386.     Boolean need20d12Defaults = true;
  387.     Boolean need20d14Defaults = true;
  388.     Boolean haveListFont, haveTextFont, differentFonts;
  389.     CStr255 msg;
  390.     short fontNum;
  391.     
  392.     err = FSpOpenDF(&gPrefsFile, fsRdPerm, &fRefNum);
  393.  
  394.     if (err == noErr) {
  395.         count = sizeof(TPrefRec);
  396.         err = FSRead(fRefNum, &count, (Ptr)&gPrefs);
  397.         if (err == noErr && count == sizeof(TPrefRec)) {
  398.             ScramblePW(gPrefs.remotePassword, sizeof(gPrefs.remotePassword));
  399.             need102Defaults = false;
  400.             if (strcmp(gPrefs.magicCookie, "MagicCookie") == 0) {
  401.                 need12NUDefaults = false;
  402.                 if (strcmp(gPrefs.version, "1.3d1") == 0) {
  403.                     need13d1Defaults = false;
  404.                 } else if (strcmp(gPrefs.version, "1.3d4") == 0 ||
  405.                     strcmp(gPrefs.version, "1.3d5") == 0 ||
  406.                     strcmp(gPrefs.version, "1.3d6") == 0) 
  407.                 {
  408.                     need13d1Defaults = false;
  409.                     need13d4Defaults = false;
  410.                 } else if (strcmp(gPrefs.version, "2.0d9") == 0) {
  411.                     need13d1Defaults = false;
  412.                     need13d4Defaults = false;
  413.                     need20d9Defaults = false;
  414.                 } else if (strcmp(gPrefs.version, "2.0d10") == 0) {
  415.                     need13d1Defaults = false;
  416.                     need13d4Defaults = false;
  417.                     need20d9Defaults = false;
  418.                     need20d10Defaults = false;
  419.                 } else if (strcmp(gPrefs.version, "2.0d11") == 0) {
  420.                     need13d1Defaults = false;
  421.                     need13d4Defaults = false;
  422.                     need20d9Defaults = false;
  423.                     need20d10Defaults = false;
  424.                     need20d11Defaults = false;
  425.                 } else if (strcmp(gPrefs.version, "2.0d12") == 0) {
  426.                     need13d1Defaults = false;
  427.                     need13d4Defaults = false;
  428.                     need20d9Defaults = false;
  429.                     need20d10Defaults = false;
  430.                     need20d11Defaults = false;
  431.                     need20d12Defaults = false;
  432.                 } else if (strcmp(gPrefs.version, "2.0d14") == 0) {
  433.                     need13d1Defaults = false;
  434.                     need13d4Defaults = false;
  435.                     need20d9Defaults = false;
  436.                     need20d10Defaults = false;
  437.                     need20d11Defaults = false;
  438.                     need20d12Defaults = false;
  439.                     need20d14Defaults = false;
  440.                 }
  441.             }
  442.         } else {
  443.             FSClose(fRefNum);
  444.             fRefNum = 0;
  445.         }
  446.     } else {
  447.         fRefNum = 0;
  448.     }
  449.     
  450.     if (need102Defaults) {
  451.         *gPrefs.newsServerName = 0;
  452.         *gPrefs.mailServerName = 0;
  453.         *gPrefs.name = 0;
  454.         *gPrefs.host = 0;
  455.         *gPrefs.fullName = 0;
  456.         *gPrefs.organization = 0;
  457.         *gPrefs.signature = 0;
  458.         *gPrefs.address = 0;
  459.         pstrcpy(gPrefs.listFont, "\pMonaco");
  460.         pstrcpy(gPrefs.textFont, "\pMonaco");
  461.         gPrefs.listSize = 9;
  462.         gPrefs.textSize = 9;
  463.         SetRect(&gPrefs.groupWindowRect, 4, 40, 300, 340);
  464.         SetPt(&gPrefs.statusWindowLocn, 48, 100);
  465.         gPrefs.groupWindowVisible = true;
  466.         gPrefs.maxFetch = 400;
  467.     }
  468.     
  469.     if (need12NUDefaults) {
  470.         gPrefs.areYouSureAlert = true;
  471.     }
  472.     
  473.     if (need13d1Defaults) {
  474.         gPrefs.autoFetchnewsrc = false;
  475.         gPrefs.replyCC = false;
  476.     }
  477.     
  478.     if (need13d4Defaults) {
  479.         gPrefs.showHeaders = false;
  480.         gPrefs.showAuthors = true;
  481.         gPrefs.showThreadsCollapsed = true;
  482.     }
  483.     
  484.     if (need20d9Defaults) {
  485.         gPrefs.checkForNewGroups = true;
  486.         gPrefs.showKilledArticles = false;
  487.         gPrefs.groupCheckTime = 0;
  488.         memset(gPrefs.remotePassword, 0, sizeof(gPrefs.remotePassword));
  489.         gPrefs.savePassword = false;
  490.         gPrefs.expandHilited = true;
  491.         gPrefs.textCreator = 'ttxt';
  492.         pstrcpy(gPrefs.textCreatorName, "\pTeachText");
  493.         GetDefaultTextDir(gPrefs.textVolName, &gPrefs.textDirID);
  494.         gPrefs.maxGroupNameWidth = 0;
  495.         gPrefs.useXPAT = false;
  496.     }
  497.     
  498.     if (need20d10Defaults) {
  499.         gPrefs.textDefaultDir = false;
  500.         strcpy(gPrefs.remotePath, ".newsrc");
  501.     }
  502.     
  503.     if (need20d11Defaults) {
  504.         gPrefs.addSigSeparatorLine = true;
  505.     }
  506.     
  507.     if (need20d12Defaults) {
  508.         gPrefs.keypadShortcuts = false;
  509.         gPrefs.logActionsToFile = false;
  510.     }
  511.     
  512.     if (need20d14Defaults) {
  513.         gPrefs.batchedGroupCmds = false;
  514.         gPrefs.noNewConnection = false;
  515.         gPrefs.noModeReader = false;
  516.     }
  517.  
  518.     strcpy(gPrefs.magicCookie, "MagicCookie");
  519.     strcpy(gPrefs.version, "2.0d14");
  520.     
  521.     if (*gPrefs.newsServerName == 0 && !DoServerDialog()) goto exit;
  522.     if (*gPrefs.address == 0 && !DoPersonalDialog()) goto exit;
  523.  
  524.     haveListFont = GetFontNumber(gPrefs.listFont, &fontNum);
  525.     haveTextFont = GetFontNumber(gPrefs.textFont, &fontNum);
  526.     differentFonts = !EqualString(gPrefs.listFont, gPrefs.textFont, false, false);
  527.     if (!haveListFont) {
  528.         sprintf(msg, "The font “%#s” does not exist on this system. Monaco will be used instead.",
  529.             gPrefs.listFont);
  530.         ErrorMessage(msg);
  531.         pstrcpy(gPrefs.listFont, "\pMonaco");
  532.     }
  533.     if (!haveTextFont) {
  534.         if (differentFonts) {
  535.             sprintf(msg, "The font “%#s” does not exist on this system. Monaco will be used instead.",
  536.                 gPrefs.textFont);
  537.             ErrorMessage(msg);
  538.         }
  539.         pstrcpy(gPrefs.textFont, "\pMonaco");
  540.     }
  541.     
  542.     pstrcpy(gNewsServerAtStartup, gPrefs.newsServerName);
  543.  
  544.     if (!PtInRect(*(Point*)&gPrefs.groupWindowRect, &gDesktopExtent)) {
  545.         SetRect(&gPrefs.groupWindowRect, 4, 40, 300, 340);
  546.     }
  547.  
  548.     if (fRefNum == 0) return true;
  549.     
  550.     if (!ReadGroupsFromPrefs(fRefNum)) goto exit;
  551.     
  552.     FSClose(fRefNum);
  553.     
  554.     return true;
  555.  
  556. exit:
  557.  
  558.     if (fRefNum != 0) FSClose(fRefNum);
  559.     return false;
  560. }
  561.  
  562.  
  563.  
  564. /*----------------------------------------------------------------------------
  565.     WriteGroups 
  566.     
  567.     Writes the full group list to the preferences file.
  568.     
  569.     Entry:    fRefNum = refnum of opened prefs file.
  570. ----------------------------------------------------------------------------*/
  571.  
  572. static void WriteGroups (short fRefNum)
  573. {
  574.     OSErr err;
  575.     Handle groupBuf = nil;
  576.     long groupBufNext, groupBufAllocated;
  577.     short i, len;
  578.     char *groupName;
  579.     
  580.     StatusWindow("Writing full group list.");
  581.     
  582.     /* Allocate and build the buffer of group names. */
  583.     
  584.     groupBuf = MyNewHandle(100000);
  585.     groupBufNext = 0;
  586.     groupBufAllocated = 100000;
  587.     for (i = 0; i < gNumGroups; i++) {
  588.         groupName = *gGroupNames + (*gGroupArray)[i].nameOffset;
  589.         len = strlen(groupName) + 1;
  590.         if (groupBufNext + len > groupBufAllocated) {
  591.             groupBufAllocated += 50000;
  592.             MySetHandleSize(groupBuf, groupBufAllocated);
  593.         }
  594.         BlockMove(groupName, *groupBuf + groupBufNext, len);
  595.         groupBufNext += len;
  596.         *(*groupBuf + groupBufNext -1) = CR;
  597.         if ((i & 0x1f) == 0) GiveTime();
  598.     }
  599.     
  600.     /* Write the buffer to the file. */
  601.     
  602.     HLock(groupBuf);
  603.     err = FSWrite(fRefNum, &groupBufNext, *groupBuf);
  604.     if (err != noErr) goto exit;
  605.     err = SetEOF(fRefNum, sizeof(TPrefRec) + groupBufNext);
  606.     if (err != noErr) goto exit;
  607.     
  608.     MyDisposHandle(groupBuf);
  609.     return;
  610.  
  611. exit:
  612.     UnexpectedErrorMessage(err);
  613.     if (groupBuf) MyDisposHandle(groupBuf);
  614. }
  615.  
  616.  
  617.  
  618. /*----------------------------------------------------------------------------
  619.     WritePrefs 
  620.     
  621.     Writes the preferences file.
  622. ----------------------------------------------------------------------------*/
  623.  
  624. void WritePrefs (void)
  625. {
  626.     OSErr err;
  627.     short fRefNum = 0;
  628.     long count;
  629.  
  630.     err = FSpOpenDF(&gPrefsFile, fsRdWrPerm, &fRefNum);
  631.     if (err == fnfErr) {
  632.         FSpCreate(&gPrefsFile, kFCreator, kPrefType, smSystemScript);
  633.         err = FSpOpenDF(&gPrefsFile, fsRdWrPerm, &fRefNum);
  634.     }
  635.     if (err != noErr) goto exit;
  636.  
  637.     if (!gPrefs.savePassword)
  638.         memset(gPrefs.remotePassword, 0, sizeof(gPrefs.remotePassword));
  639.     ScramblePW(gPrefs.remotePassword, sizeof(gPrefs.remotePassword));
  640.  
  641.     count = sizeof(TPrefRec);
  642.     err = FSWrite(fRefNum, &count, &gPrefs);
  643.     if (err != noErr) goto exit;
  644.     
  645.     /* If the user changed news servers, save an empty full group list to force the
  646.        program to read a new full group list the next time it is run. */
  647.     
  648.     if (!EqualString(gPrefs.newsServerName, gNewsServerAtStartup, false, true)) {
  649.         gNumGroups = 0;
  650.         gFullGroupListDirty = true;
  651.     }
  652.     
  653.     if (gStartupOK && gFullGroupListDirty) WriteGroups(fRefNum);
  654.     
  655.     FSClose(fRefNum);
  656.     FlushVol(nil, gPrefsFile.vRefNum);
  657.     return;
  658.     
  659. exit:
  660.  
  661.     if (fRefNum != 0) FSClose(fRefNum);
  662.     FlushVol(nil, gPrefsFile.vRefNum);
  663.     UnexpectedErrorMessage(err);
  664. }
  665.